package main

import (
	"crypto/cipher"
	"strconv"
)

const BlockSize = 16

type Key []byte

type KeySizeError int

type Cipher struct {
	subkeys []uint32
	block1  []uint32
	block2  []byte
}

var fk = [4]uint32{
	4112222614, 836215495, 3574444875, 2993693404,
}

var ck = [32]uint32{
	472130596, 605842556, 1818502180, 606870764, 4242859044, 607923228, 1818518628, 606878764, 484762852, 3827051580, 740582500, 1684806700, 1009001700, 3841780764, 740566052, 1684831340, 472130596, 605875452, 3974374436, 606870636, 2086954020, 607923228, 3974423780, 606878764, 476341348, 1679567932, 740615396, 3840678956, 1009001572, 1685875740, 740566052, 1684763257,
}

var sbox = [256]uint8{
	70, 121, 23, 50, 45, 220, 138, 161, 160, 162, 214, 234, 211, 215, 41, 46, 76, 253, 236, 92, 148, 186, 199, 105, 238, 87, 53, 111, 207, 128, 159, 5, 222, 18, 164, 101, 126, 119, 226, 73, 103, 95, 72, 174, 34, 99, 206, 134, 87, 175, 181, 96, 193, 224, 125, 21, 95, 75, 110, 143, 250, 176, 153, 225, 64, 160, 91, 15, 128, 100, 173, 57, 218, 101, 37, 255, 99, 202, 231, 192, 3, 234, 51, 195, 21, 190, 81, 115, 19, 228, 68, 59, 38, 203, 168, 43, 58, 42, 80, 61, 59, 137, 115, 135, 7, 94, 71, 58, 32, 89, 255, 83, 212, 70, 17, 200, 76, 244, 117, 30, 122, 52, 229, 71, 100, 12, 86, 116, 85, 53, 88, 146, 135, 255, 141, 22, 84, 5, 60, 55, 152, 116, 180, 65, 78, 243, 249, 63, 175, 46, 79, 248, 62, 161, 2, 197, 121, 61, 82, 254, 235, 20, 204, 172, 228, 172, 170, 160, 233, 10, 136, 166, 94, 29, 33, 186, 14, 236, 114, 155, 35, 115, 161, 44, 252, 149, 24, 31, 1, 55, 10, 220, 150, 180, 61, 41, 102, 97, 195, 110, 200, 133, 29, 94, 15, 74, 130, 210, 203, 240, 185, 45, 104, 182, 198, 144, 89, 164, 194, 170, 93, 81, 4, 57, 224, 254, 221, 70, 154, 225, 9, 27, 220, 72, 248, 204, 171, 168, 66, 156, 232, 141, 145, 214, 230, 145, 109, 89, 151, 177, 97, 233, 28, 242, 113, 72,
}

var sbox0 = [256]uint32{
	1192827230, 2028332445, 391928907, 852019450, 766817433, 3748885420, 2284464802, 2743502373, 2726461984, 2693433898, 3579534222, 3920341826, 3494858655, 3563020171, 698655885, 783857814, 1295069564, 4277663497, 4021531484, 1567715628, 2521977540, 3102403154, 3290374107, 1755686349, 3988503382, 1448959243, 903140577, 1857928659, 3426697203, 2181169792, 2642312931, 85201937, 3715857318, 306727002, 2794623540, 1687524849, 2147088774, 1994251691, 3784018794, 1210393965, 1721605627, 1585282339, 1226908008, 2897918486, 579373226, 1653444079, 3443211254, 2216303262, 1448959243, 2914958867, 3084310113, 1635877344, 3255240645, 3817046880, 2096493961, 357848129, 1585282339, 1244474727, 1874442710, 2369666739, 4192987922, 2999108208, 2607179517, 3800532837, 1090584896, 2726461984, 1517120823, 255605811, 2181169792, 1704038900, 2947986969, 971302109, 3647695794, 1687524849, 630494385, 4244635395, 1653444079, 3375049698, 3835666299, 3271754688, 51121167, 3920341826, 869059839, 3222212559, 357848129, 3170564678, 1346716949, 1926090175, 323767391, 3885208436, 1158746452, 1005382871, 647534782, 3358535655, 2862785032, 732736647, 988342482, 715696258, 1363230992, 1039463625, 1005382871, 2334533293, 1926090175, 2233343643, 119282715, 1601796390, 1176313179, 988342482, 545292448, 1483040061, 4244635395, 1380797727, 3612562308, 1192827230, 289686613, 3408077800, 1295069564, 4157854500, 1960170913, 511211622, 2078927250, 886100196, 3868694385, 1176313179, 1704038900, 204484668, 1465473294, 1976684964, 1414878465, 903140577, 1499554104, 2420787930, 2233343643, 4244635395, 2402694841, 374888526, 1431392516, 85201937, 1022423244, 937221355, 2590139128, 1976684964, 3067269732, 1074070853, 1329150326, 4040150847, 4209501981, 1073544387, 2914958867, 783857814, 1312636275, 4226016024, 1056504006, 2743502373, 34080778, 3323402193, 2028332445, 1039463625, 1397311770, 4261149446, 3903827783, 340807748, 3476239356, 2930946588, 3885208436, 2930946588, 2829756930, 2726461984, 3936855885, 170403874, 2317492904, 2761595454, 1601796390, 494171241, 562332837, 3102403154, 238565430, 4021531484, 1942604218, 2574151415, 596413615, 1926090175, 2743502373, 749777052, 4294177548, 2539017921, 408969336, 528252003, 17040389, 937221355, 170403874, 3748885420, 2488949454, 3067269732, 1039463625, 698655885, 1738119678, 1619363301, 3222212559, 1874442710, 3408077800, 2266371729, 494171241, 1601796390, 255605811, 1260988770, 2148141706, 3511372698, 3358535655, 4089692976, 3152471645, 766817433, 1772200392, 3034241646, 3306888158, 2453816016, 1483040061, 2794623540, 3238726602, 2829756930, 1551201577, 1346716949, 68161556, 971302109, 3817046880, 4261149446, 3732371369, 1192827230, 2557111026, 3800532837, 153363501, 460090487, 3748885420, 1226908008, 4226016024, 3476239356, 2846797319, 2862785032, 1124665674, 2658300652, 3953369928, 2402694841, 2470856405, 3579534222, 3852180350, 2470856405, 1823847897, 1483040061, 2505989835, 3016148597, 1619363301, 3936855885, 477130860, 4056664890, 1892009397, 1226908008,
}

var sbox1 = [256]uint32{
	421092935, 3857030520, 1549552407, 3368614450, 3031734573, 1936960735, 707437192, 2256938403, 2189566114, 2324310688, 1532726997, 2880127721, 1330618320, 1600097236, 2762247465, 3099104814, 825326669, 4160162302, 3014876399, 1903242333, 1381155990, 3941225144, 522181572, 2779106664, 3149616877, 1566378838, 3570721077, 3183334254, 1061155788, 33718402, 2122245021, 336859397, 2071701213, 1212701202, 2459055270, 2509631844, 4193879679, 3722292086, 2341169889, 623209800, 2644376422, 2105353054, 555837513, 3132757676, 2290657826, 2374889314, 993785549, 437952132, 1566378838, 3200127917, 3604373943, 2172772449, 117949890, 2206425315, 4126509436, 1414807829, 2105353054, 757950282, 3115964015, 1044296589, 3958051577, 3267522738, 1718025627, 2273797602, 16859201, 2189566114, 1835874138, 1010578191, 33718402, 2442261605, 3065387439, 3840204089, 1802220249, 2509631844, 2492772645, 4294902780, 2374889314, 724296393, 2678029284, 50577603, 202116867, 2880127721, 3435986739, 252694464, 1414807829, 4210706108, 1162155344, 3452813170, 1280073491, 2475914471, 286348357, 3974944571, 2560146982, 791668680, 2728528042, 2896987947, 3907572282, 2829615658, 1094783057, 4109683005, 3974944571, 640069003, 3452813170, 505322373, 471603975, 2037982815, 488463174, 3907572282, 2155913248, 1701133656, 4294902780, 1296899922, 1397982423, 421092935, 1145328913, 589555915, 825326669, 3553830135, 3587547508, 2021156382, 3924398715, 3503350836, 2543284710, 488463174, 2442261605, 808467468, 1499008599, 3520177269, 1431634260, 3570721077, 1633761369, 1246419600, 505322373, 4294902780, 909556111, 1482182166, 1364264021, 336859397, 4042312764, 3705465655, 1650653338, 3520177269, 3537003702, 84231488, 960067151, 3486466032, 3890683386, 4244423487, 3200127917, 3099104814, 1027437390, 3823311099, 4177053246, 2256938403, 134744578, 387436998, 3857030520, 4109683005, 1229527635, 4227532541, 2947500008, 1347437588, 859045071, 2998017198, 2475914471, 2998017198, 2863268520, 2189566114, 2812759530, 673718794, 572696714, 2593799844, 2037982815, 1953786141, 2223285537, 3941225144, 943207950, 3014876399, 3385440883, 1852766105, 2358030115, 3452813170, 2256938403, 2964364332, 4092792063, 1448526231, 1616934936, 2088526623, 67372289, 3705465655, 673718794, 1936960735, 1515900564, 3537003702, 4109683005, 2762247465, 2577006183, 2240144736, 252694464, 3115964015, 589555915, 370577799, 1953786141, 2037982815, 1010578191, 690577995, 168462976, 1263246033, 791668680, 3284349171, 3873856955, 3031734573, 2711734377, 3671748276, 454811333, 1111675026, 1701133656, 2459055270, 185322177, 2863268520, 1970612572, 1162155344, 269489156, 3840204089, 2206425315, 4227532541, 2004330974, 421092935, 1785393816, 2273797602, 606350601, 1819047707, 1936960735, 555837513, 3823311099, 859045071, 2930640809, 2728528042, 151603779, 1920134302, 2745387243, 909556111, 1179047315, 1532726997, 2610659045, 1179047315, 3048593772, 1701133656, 1583270805, 3334895027, 2240144736, 2812759530, 1886415900, 3419093745, 3318068592, 555837513,
}

var sbox2 = [256]uint32{
	425608985, 3852302565, 1548425052, 3371840200, 3029937588, 1940709235, 715294762, 2250613638, 2183176834, 2318049418, 1536087387, 2873289131, 1335873615, 1602999391, 2760714660, 3096850104, 830229809, 4144627447, 3009212339, 1898732913, 1388615250, 3931289834, 534496287, 2781702309, 3143036347, 1561024093, 3571529172, 3184750269, 1072942143, 41976322, 2128846206, 336659732, 2074533243, 1213862472, 2452924050, 2515625109, 4186341369, 3719001821, 2339037579, 627918885, 2650498717, 2099469949, 560482593, 3122048186, 2292851336, 2381275789, 1006030139, 446596122, 1561024093, 3188960702, 3596728278, 2178965889, 130400775, 2204164995, 4119428341, 1413551444, 2099469949, 761743917, 3117838265, 1051954494, 3943889387, 3262165698, 1727896422, 2271601287, 20988161, 2183176834, 1832344173, 1009979196, 41976322, 2448713105, 3055136694, 3839703524, 1806883179, 2515625109, 2494637460, 4278451455, 2381275789, 736282923, 2675696799, 62964483, 202310412, 2873289131, 3439277004, 265273359, 1413551444, 4198939898, 1159024709, 3451876045, 1281299276, 2473912211, 290735377, 3973528556, 2562598552, 803719215, 2718476962, 2894539692, 3906091752, 2827102888, 1091588417, 4106829300, 3973528556, 648907558, 3451876045, 513508638, 471533340, 2032557945, 492520989, 3906091752, 2157977728, 1698519141, 4278451455, 1293898317, 1401214803, 425608985, 1146425668, 602458915, 830229809, 3542415315, 3584128213, 2019958392, 3918691305, 3504616656, 2540824215, 492520989, 2448713105, 809241648, 1494112089, 3517216209, 1426150485, 3571529172, 1631082849, 1255837770, 513508638, 4278451455, 918130486, 1481512536, 1359238481, 336659732, 4039916784, 3706402780, 1660459618, 3517216209, 3529815762, 88424453, 964054841, 3477074127, 3877501671, 4240654332, 3188960702, 3096850104, 1030966845, 3810065379, 4173741816, 2250613638, 134873608, 399623703, 3852302565, 4106829300, 1226462025, 4211539451, 2940725423, 1346638928, 872206131, 2988224178, 2473912211, 2988224178, 2852300970, 2183176834, 2806901415, 673319464, 581470754, 2587796634, 2032557945, 1953045876, 2225414532, 3931289834, 943066680, 3009212339, 3384439753, 1861720430, 2360288140, 3451876045, 2250613638, 2963025072, 4077715443, 1455527766, 1618483296, 2086870908, 67436804, 3706402780, 673319464, 1940709235, 1523487834, 3529815762, 4106829300, 2760714660, 2583586713, 2246402181, 265273359, 3117838265, 602458915, 378636054, 1953045876, 2032557945, 1009979196, 694307625, 176848906, 1268437323, 803719215, 3274765251, 3864902630, 3029937588, 2714266017, 3664688346, 467584283, 1120965186, 1698519141, 2452924050, 197837067, 2852300970, 1965644917, 1159024709, 269747216, 3839703524, 2204164995, 4211539451, 2007621239, 425608985, 1794283626, 2271601287, 606931236, 1819745132, 1940709235, 560482593, 3810065379, 872206131, 2919737774, 2718476962, 155861769, 1928109682, 2739465123, 918130486, 1188401990, 1536087387, 2608784795, 1188401990, 3050925237, 1698519141, 1590400350, 3329602502, 2246402181, 2806901415, 1886133360, 3409637835, 3317002437, 560482593,
}

var sbox3 = [256]uint32{
	1581717785, 2641946085, 1259822172, 4197632200, 2569909428, 2900325235, 2726832682, 631473798, 547521154, 715164298, 2396347227, 1122610091, 2681229135, 2345951071, 2368316580, 2519644344, 2085433649, 167704567, 1559212979, 744321393, 3298185810, 1387850474, 3687063327, 3446187429, 1458420667, 190209373, 3778401492, 3547250109, 4090249023, 2156003842, 3818749566, 285545492, 2799532923, 1511147592, 883331730, 4049900949, 2256534009, 2876693981, 1793166219, 1833444645, 4217806237, 593395069, 1749623073, 380418746, 2854389896, 4016213389, 4140645179, 2659457562, 190209373, 330153662, 1639438038, 3764486529, 3317827335, 1625523075, 2306668021, 1091916884, 593395069, 1732914477, 3597646265, 3012378174, 318368747, 1890763458, 4254819942, 1709344647, 1078001921, 547521154, 928673133, 856636476, 2156003842, 4100297105, 430945974, 3711558884, 3000593259, 4049900949, 2972030100, 66912255, 4016213389, 3804834603, 2078580639, 3234005763, 251857932, 1122610091, 4281584844, 3485470479, 1091916884, 1186790138, 357582149, 3211972045, 1595100236, 1961333651, 1413812497, 3611028716, 3190200472, 3888656175, 145400482, 2267786412, 3527076072, 2183833768, 273760577, 3376280820, 3611028716, 2911577638, 3211972045, 2609192478, 453450780, 643791225, 1531321629, 3527076072, 2686484608, 1029203301, 66912255, 525487437, 2228704083, 1581717785, 1427194948, 3905626915, 2085433649, 620221395, 2708788693, 1713272952, 2457594345, 3828666576, 1910937495, 1531321629, 4100297105, 1007431728, 240605529, 2759184849, 22304085, 3778401492, 945381729, 3666889290, 2609192478, 66912255, 3113170486, 1310087256, 72700241, 285545492, 3426545904, 3946306780, 4170867298, 2759184849, 1689703122, 1161823493, 1984903481, 1072746447, 502982631, 3275750652, 330153662, 2519644344, 1934507325, 419161059, 3326015736, 631473798, 167905288, 3519420183, 2641946085, 3376280820, 441665865, 117308411, 1206431663, 1142181968, 4241437491, 481211058, 1961333651, 481211058, 44608170, 547521154, 1307223975, 571090984, 2827624994, 1050974874, 643791225, 1763538036, 2770437252, 1387850474, 906901560, 1559212979, 3128150473, 4154027630, 2938342540, 3211972045, 631473798, 2620174512, 218100723, 3247920726, 2014863456, 1663007868, 83952644, 3946306780, 571090984, 2900325235, 3465828954, 1689703122, 3376280820, 2368316580, 4268202393, 3848308101, 3485470479, 3597646265, 3905626915, 2441549334, 1763538036, 643791225, 856636476, 1649092905, 2323646986, 2597407563, 3888656175, 821281731, 1572595430, 2569909428, 3362365857, 1857346266, 3737459483, 3499246146, 1029203301, 883331730, 3401648907, 44608170, 693925237, 357582149, 335810576, 3711558884, 1625523075, 117308411, 2849929079, 1581717785, 4070074986, 1709344647, 755573796, 1998285932, 2900325235, 1749623073, 419161059, 4241437491, 128560814, 145400482, 1245907209, 3969806962, 1223402403, 3113170486, 3583198790, 2396347227, 2128976795, 3583198790, 3647780277, 1029203301, 3415563870, 1974716102, 3848308101, 1307223975, 1813803120, 988924875, 3044066757, 1749623073,
}

func rl(x uint32, i uint8) uint32 { return (x << (i % 32)) | (x >> (32 - (i % 32))) }

func l0(b uint32) uint32 { return b ^ rl(b, 13) ^ rl(b, 23) }

func feistel0(x0, x1, x2, x3, rk uint32) uint32 { return x0 ^ l0(p(x1^x2^x3^rk)) }

func p(a uint32) uint32 {
	return (uint32(sbox[a>>24]) << 24) ^ (uint32(sbox[(a>>16)&0xff]) << 16) ^ (uint32(sbox[(a>>8)&0xff]) << 8) ^ uint32(sbox[(a)&0xff])
}

func permuteInitialBlock(b []uint32, block []byte) {
	for i := 0; i < 4; i++ {
		b[i] = (uint32(block[i*4]) << 24) | (uint32(block[i*4+1]) << 16) |
			(uint32(block[i*4+2]) << 8) | (uint32(block[i*4+3]))
	}
}

func permuteFinalBlock(b []byte, block []uint32) {
	for i := 0; i < 4; i++ {
		b[i*4] = uint8(block[i] >> 24)
		b[i*4+1] = uint8(block[i] >> 16)
		b[i*4+2] = uint8(block[i] >> 8)
		b[i*4+3] = uint8(block[i])
	}
}

func cryptBlock(subkeys []uint32, b []uint32, r []byte, dst, src []byte, decrypt bool) {
	permuteInitialBlock(b, src)

	_ = b[3]
	if decrypt {
		for i := 0; i < 8; i++ {
			s := subkeys[31-4*i-3 : 31-4*i-3+4]
			x := b[1] ^ b[2] ^ b[3] ^ s[3]
			b[0] = b[0] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
			x = b[0] ^ b[2] ^ b[3] ^ s[2]
			b[1] = b[1] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
			x = b[0] ^ b[1] ^ b[3] ^ s[1]
			b[2] = b[2] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
			x = b[1] ^ b[2] ^ b[0] ^ s[0]
			b[3] = b[3] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
		}
	} else {
		for i := 0; i < 8; i++ {
			s := subkeys[4*i : 4*i+4]
			x := b[1] ^ b[2] ^ b[3] ^ s[0]
			b[0] = b[0] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
			x = b[0] ^ b[2] ^ b[3] ^ s[1]
			b[1] = b[1] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
			x = b[0] ^ b[1] ^ b[3] ^ s[2]
			b[2] = b[2] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
			x = b[1] ^ b[2] ^ b[0] ^ s[3]
			b[3] = b[3] ^ sbox0[x&0xff] ^ sbox1[(x>>8)&0xff] ^ sbox2[(x>>16)&0xff] ^ sbox3[(x>>24)&0xff]
		}
	}
	b[0], b[1], b[2], b[3] = b[3], b[2], b[1], b[0]
	permuteFinalBlock(r, b)
	copy(dst, r)
}

func generateSubKeys(key []byte) []uint32 {
	subkeys := make([]uint32, 32)
	b := make([]uint32, 4)
	permuteInitialBlock(b, key)
	b[0] ^= fk[0]
	b[1] ^= fk[1]
	b[2] ^= fk[2]
	b[3] ^= fk[3]
	for i := 0; i < 32; i++ {
		subkeys[i] = feistel0(b[0], b[1], b[2], b[3], ck[i])
		b[0], b[1], b[2], b[3] = b[1], b[2], b[3], subkeys[i]
	}
	return subkeys
}

func (k KeySizeError) Error() string {
	return "Cipher: invalid key size " + strconv.Itoa(int(k))
}

func Init() {
	for i := len(sbox) - 2; i >= 0; i-- {
		sbox[i] ^= sbox[i + 1]
	}
	for i := len(sbox0) - 2; i >= 0; i-- {
		sbox0[i] ^= sbox0[i + 1]
	}
	for i := len(sbox1) - 2; i >= 0; i-- {
		sbox1[i] ^= sbox1[i + 1]
	}
	for i := len(sbox2) - 2; i >= 0; i-- {
		sbox2[i] ^= sbox2[i + 1]
	}
	for i := len(sbox3) - 2; i >= 0; i-- {
		sbox3[i] ^= sbox3[i + 1]
	}
}

func NewCipher(key []byte) (cipher.Block, error) {
	if len(key) != BlockSize {
		return nil, KeySizeError(len(key))
	}
	c := new(Cipher)
	c.subkeys = generateSubKeys(key)
	c.block1 = make([]uint32, 4)
	c.block2 = make([]byte, 16)
	return c, nil
}

func (c *Cipher) BlockSize() int {
	return BlockSize
}

func (c *Cipher) Encrypt(dst, src []byte) {
	cryptBlock(c.subkeys, c.block1, c.block2, dst, src, false)
}

func (c *Cipher) Decrypt(dst, src []byte) {
	cryptBlock(c.subkeys, c.block1, c.block2, dst, src, true)
}
